#include <string.h>
#include "gkt_defines.h"
#include "gkt_debug.h"
#include "gkt_malloc.h"
#include "gkt_window.h"
#include "widget_draw.h"

enum {
	WIDGET_LIST_ITEMS_NULL = 0,
	WIDGET_LIST_ITEMS_STATIC,
	WIDGET_LIST_ITEMS_DYNAMIC
};

#ifdef GKT_CONFIG_WIDGET_LIST_ITEM_MAXNUMS
#define WIDGET_LIST_ITEM_MAXNUMS	GKT_CONFIG_WIDGET_LIST_ITEM_MAXNUMS
#else
#define WIDGET_LIST_ITEM_MAXNUMS	10
#endif

#if defined(GKT_CONFIG_SCREEN_ENABLE) && GKT_CONFIG_SCREEN_ENABLE

#if defined(GKT_CONFIG_WIDGET_LIST_ITEM_INDEX_ENABLE) \
	&& GKT_CONFIG_WIDGET_LIST_ITEM_INDEX_ENABLE
#define WIDGET_LIST_ITEM_INDEX_ENABLE	1
#else
#define WIDGET_LIST_ITEM_INDEX_ENABLE	0
#endif

#if defined(GKT_CONFIG_WIDGET_LIST_SCROLL_LOOPBACK_ENABLE) \
	&& GKT_CONFIG_WIDGET_LIST_SCROLL_LOOPBACK_ENABLE
#define WIDGET_LIST_SCROLL_LOOPBACK_ENABLE	1
#else
#define WIDGET_LIST_SCROLL_LOOPBACK_ENABLE	0
#endif

typedef struct _widget_list_private_data {
	uint32_t	item_max_nums;
	uint32_t	item_actual_nums;
	uint32_t	item_visible_max_nums;
	uint32_t	item_visible_nums;
	uint32_t	item_visible_start;	/* start index */
	uint32_t	item_focus_index;

	const gkt_widget_list_attr_s *const_attr;
	gkt_widget_list_item_s	*dynamic_items;
} widget_list_private_data_s;

typedef struct _widget_list_paint_params {
	uint32_t	item_index;
	uint32_t	position;
	uint32_t	focus;
	uint32_t	update;
} widget_list_paint_params_s;

#define WIDGET_LIST_TEXT_MAXSIZE	\
	((GKT_SCREEN_WIDTH / GKT_WIDGET_FONT_MINSIZE) * 2)

static void widget_list_paint_item(gkt_widget_s *widget, 
					widget_list_private_data_s *private_data,
					widget_list_paint_params_s *params)
{
	const gkt_widget_list_item_s *list_item;
	const char *text;
#if WIDGET_LIST_ITEM_INDEX_ENABLE
	char full_text[WIDGET_LIST_TEXT_MAXSIZE + 1];
	uint32_t item_index, index_size, text_len, offset;
#endif
	gkt_rect_s rect;

	gkt_trace("widget_list_paint_item: index(%u - %u - %u - %u)\n", 
				params->item_index, params->position,
				params->focus, params->update);

	if (__LIKELY(private_data->item_actual_nums)) {
		list_item = private_data->dynamic_items ? 
						&private_data->dynamic_items[params->item_index] : 
						&private_data->const_attr->const_items[params->item_index];
		text = GKT_TEXT(list_item->text);
		if (__UNLIKELY(!text)) {
			gkt_error("widget_list: (%u @ %u) text NULL.\n", 
						widget->id, params->item_index);
			return;
		}
	}
	else
		text = "---";

#if WIDGET_LIST_ITEM_INDEX_ENABLE
	item_index = params->item_index + 1;
	index_size = (item_index < 10) ? 1 : 2;

	text_len = strlen(text);
	if ((index_size + 1 + text_len) > WIDGET_LIST_TEXT_MAXSIZE)
		text_len = WIDGET_LIST_TEXT_MAXSIZE - (index_size + 1);

	full_text[WIDGET_LIST_TEXT_MAXSIZE] = '\0';

	offset = WIDGET_LIST_TEXT_MAXSIZE - text_len;
	if (text_len > 0)
		memcpy(&full_text[offset], text, text_len);
	
	full_text[--offset] = '.';

	do {
		full_text[--offset] = '0' + (item_index % 10);
		item_index /= 10;
		index_size--;
	} while ((item_index > 0) && (index_size > 0));

	text = &full_text[offset];
#endif

	rect.x = GKT_WIDGET_X(widget) + GKT_WIDGET_TEXT_X_DIST;
	rect.y = GKT_WIDGET_Y(widget) + (private_data->const_attr->item_height * params->position);
	rect.width = GKT_WIDGET_WIDTH(widget) - (GKT_WIDGET_TEXT_X_DIST << 1);
	rect.height = (uint16_t)private_data->const_attr->item_height;

	widget_draw_background(widget, &rect, params->focus, 0);
	widget_draw_text(widget, &rect, text, ~0, params->focus, 0);
	if (params->update)
		gkt_screen_update(&rect);
}

static void widget_list_focus_item(gkt_widget_s *widget, 
					widget_list_private_data_s *private_data,
					uint32_t new_focus_index)
{
	widget_list_paint_params_s params;

	gkt_trace("widget_list_focus_item: (0x%x) %u --> %u\n", 
				widget->flags, private_data->item_focus_index, 
				new_focus_index);

	if (__UNLIKELY(new_focus_index == private_data->item_focus_index))
		return;

	if ((widget->flags & GKT_WIDGET_F_SHOW) 
		&& (widget->flags & GKT_WIDGET_F_FOCUS)) 
	{
		params.update = 1;
		if (new_focus_index < private_data->item_visible_start) {
			private_data->item_visible_start = new_focus_index;

			params.item_index = new_focus_index;
			params.position = 0;
			params.focus = 1;
			while (params.position < private_data->item_visible_nums) {
				widget_list_paint_item(widget, private_data, &params);
				params.item_index++;
				params.position++;
				params.focus = 0;
			}
		}
		else if (new_focus_index < (private_data->item_visible_start + private_data->item_visible_nums)) {
			params.item_index = private_data->item_focus_index;
			params.position = private_data->item_focus_index - private_data->item_visible_start;
			params.focus = 0;
			widget_list_paint_item(widget, private_data, &params);

			params.item_index = new_focus_index;
			params.position = new_focus_index - private_data->item_visible_start;
			params.focus = 1;
			widget_list_paint_item(widget, private_data, &params);
		}
		else {
			private_data->item_visible_start = new_focus_index - private_data->item_visible_nums + 1;

			params.item_index = private_data->item_visible_start;
			params.position = 0;
			while (params.position < private_data->item_visible_nums) {
				params.focus = (params.item_index < new_focus_index) ? 0 : 1;
				widget_list_paint_item(widget, private_data, &params);
				params.item_index++;
				params.position++;
			}
		}
	}
	else {
		if (new_focus_index < private_data->item_visible_start)
			private_data->item_visible_start = new_focus_index;
		else if (new_focus_index >= (private_data->item_visible_start + private_data->item_visible_nums))
			private_data->item_visible_start = new_focus_index - private_data->item_visible_nums + 1;
	}

	private_data->item_focus_index = new_focus_index;

	if (widget->flags & GKT_WIDGET_F_ITEM_FOCUS) {
		gkt_window_custom_event_handle(widget->window, 
				GKT_EVENT_WIDGET, GKT_WIDGET_EVENT_ITEM_FOCUS, 
				(uint32_t)widget->id, (uint32_t)private_data->item_focus_index);
	}
}

static void widget_list_paint(gkt_widget_s *widget,
					widget_list_private_data_s *private_data,
					uint32_t update)
{
	widget_list_paint_params_s paint_params;

	paint_params.item_index = private_data->item_visible_start;
	paint_params.position = 0;
	paint_params.update = update;

	if (__LIKELY(private_data->item_actual_nums)) {
		while (paint_params.position < (uint32_t)private_data->item_visible_nums) {
			if ((widget->flags & GKT_WIDGET_F_FOCUS) 
				&& (paint_params.item_index == private_data->item_focus_index))
			{
				paint_params.focus = 1;
			}
			else
				paint_params.focus = 0;
			widget_list_paint_item(widget, private_data, &paint_params);

			paint_params.item_index++;
			paint_params.position++;
		}
	}
	else {
		paint_params.focus = 0;
		widget_list_paint_item(widget, private_data, &paint_params);
	}
}

int gkt_widget_list_event_handler(gkt_widget_s *widget, 
					uint32_t major_type, uint32_t minor_type, 
					uint32_t data)
{
	widget_list_private_data_s *private_data;
	gkt_widget_list_item_s *dynamic_items;
	uint32_t index;
	int retval = GKT_EVENT_HANDLE_CONTINUE;

	GKT_ASSERT(widget->desc->attr);
	private_data = (widget_list_private_data_s *)widget->private_data;

	if (GKT_EVENT_WIDGET == major_type) {
		retval = GKT_EVENT_HANDLE_COMPLETE;

		if (GKT_WIDGET_EVENT_INIT == minor_type) {
			widget->flags |= GKT_WIDGET_F_ALIGN_CENTER;

			private_data = gkt_malloc(sizeof(widget_list_private_data_s));
			if (__LIKELY(private_data)) {
				memset((void *)private_data, 0, sizeof(widget_list_private_data_s));

				private_data->const_attr = (const gkt_widget_list_attr_s *)widget->desc->attr;
				private_data->item_visible_max_nums = widget->desc->rect.height / private_data->const_attr->item_height;
				if (private_data->const_attr->item_nums 
					&& private_data->const_attr->const_items) 
				{
					if (private_data->const_attr->item_nums <= WIDGET_LIST_ITEM_MAXNUMS)
						private_data->item_max_nums = private_data->const_attr->item_nums;
					else
						private_data->item_max_nums = WIDGET_LIST_ITEM_MAXNUMS;
					private_data->item_actual_nums = private_data->item_max_nums;
					private_data->item_visible_nums = gkt_min(private_data->item_visible_max_nums, 
																private_data->item_actual_nums);
				}
				widget->private_data = (uint32_t)private_data;
			}
			else
				retval = GKT_ENOMEMORY;
		}
		else if (__LIKELY(private_data)) {
			if (GKT_WIDGET_EVENT_UNINIT == minor_type) {
				if (private_data->dynamic_items)
					gkt_free(private_data->dynamic_items);
				gkt_free(private_data);
			}
			else if (GKT_WIDGET_EVENT_SHOW == minor_type)
				widget_list_paint(widget, private_data, data);
			else if (GKT_WIDGET_EVENT_HIDE == minor_type) {
				if (data & GKT_WIDGET_PAINT_EXCUTE)
					widget_draw_clear(widget, data & GKT_WIDGET_PAINT_UPDATE ? 1 : 0);
			}
			else if (GKT_WIDGET_EVENT_SET_ATTR == minor_type) {
				if (private_data->const_attr
					&& (private_data->const_attr->item_nums > 0))
				{
					gkt_info("list - SET_ATTR: static defined, not permit!\n");
					return GKT_EPERMIT;
				}

				if (!data || (data > WIDGET_LIST_ITEM_MAXNUMS)) {
					gkt_trace("list - SET_ATTR: item maxnums should be in range (0, %u]\n", 
									WIDGET_LIST_ITEM_MAXNUMS);
					return GKT_EPARAM;
				}

				dynamic_items = gkt_malloc(sizeof(gkt_widget_list_item_s) * data);
				if (dynamic_items) {
					/* free old dynamic items */
					if (private_data->dynamic_items)
						gkt_free(private_data->dynamic_items);

					private_data->item_max_nums = data;
					private_data->item_actual_nums = 0;
					private_data->item_visible_nums = 0;
					private_data->item_visible_start = 0;
					private_data->item_focus_index = 0;
					private_data->dynamic_items = dynamic_items;

					if (widget->flags & GKT_WIDGET_F_SHOW)
						widget_draw_background(widget, NULL, 0, 1);
				}
				else {
					gkt_trace("list - SET_ATTR: no memory to allocate dynamic items!\n");
					retval = GKT_ENOMEMORY;
				}
			}
		}
	}
	else if (GKT_EVENT_KEY == major_type) {
		if (private_data 
			&& private_data->item_actual_nums) 
		{
			switch (minor_type) {
				case GKT_KEY_OK:
					retval = gkt_window_custom_event_handle(widget->window, 
									GKT_EVENT_WIDGET, GKT_WIDGET_EVENT_CLICK, 
									(uint32_t)widget->id, 
									(uint32_t)private_data->item_focus_index);
					break;
				case GKT_KEY_UP:
					retval = GKT_EVENT_HANDLE_COMPLETE;
					if (private_data->item_focus_index)
						index = private_data->item_focus_index - 1;
					else
#if WIDGET_LIST_SCROLL_LOOPBACK_ENABLE
						index = private_data->item_actual_nums - 1;
#else
						break;
#endif
					widget_list_focus_item(widget, private_data, index);
					break;
				case GKT_KEY_DOWN:
					retval = GKT_EVENT_HANDLE_COMPLETE;
					if (private_data->item_focus_index < (private_data->item_actual_nums - 1))
						index = private_data->item_focus_index + 1;
					else
#if WIDGET_LIST_SCROLL_LOOPBACK_ENABLE
						index = 0;
#else
						break;
#endif
					widget_list_focus_item(widget, private_data, index);
					break;
				default:
					break;
			}
		}
	}

	return retval;
}

int gkt_widget_list_add_item(gkt_widget_id_t widget_id,
				const gkt_widget_list_item_s *item)
{

	gkt_widget_s *widget;
	widget_list_private_data_s *private_data;
	widget_list_paint_params_s paint_params;
	uint32_t item_index;
	int retval = GKT_SUCCESS;

	widget = gkt_widget_by_id(widget_id);
	if (widget && item) {
		private_data = (widget_list_private_data_s *)widget->private_data;
		if (__LIKELY(private_data) 
			&& private_data->dynamic_items
			&& (private_data->item_actual_nums < private_data->item_max_nums)) 
		{
			item_index = private_data->item_actual_nums++;
			private_data->dynamic_items[item_index].text = item->text;
			private_data->dynamic_items[item_index].data = item->data;
			
			if (private_data->item_visible_nums < private_data->item_visible_max_nums) {
				private_data->item_visible_nums++;
				if (widget->flags & GKT_WIDGET_F_SHOW) {
					paint_params.item_index = item_index;
					paint_params.position = item_index - private_data->item_visible_start;
					if ((widget->flags & GKT_WIDGET_F_FOCUS)
						&& (1 == private_data->item_visible_nums))
					{
						paint_params.focus = 1;
					}
					else
						paint_params.focus = 0;
					paint_params.update = 1;
					widget_list_paint_item(widget, private_data, &paint_params);
				}
			}
		}
		else
			retval = GKT_EPERMIT;
	} 
	else
		retval = GKT_EPARAM;

	return retval;
}

int gkt_widget_list_delete_item(gkt_widget_id_t widget_id, 
				uint32_t item_index)
{
	gkt_widget_s *widget;
	widget_list_private_data_s *private_data;
	uint32_t i;
	int retval = GKT_SUCCESS;

	widget = gkt_widget_by_id(widget_id);
	if (widget) {
		private_data = (widget_list_private_data_s *)widget->private_data;
		if (__LIKELY(private_data)
			&& private_data->dynamic_items 
			&& (item_index < private_data->item_actual_nums)) 
		{
			/* delete item from list */
			private_data->item_actual_nums--;
			for (i = item_index; i < private_data->item_actual_nums; i++) {
				private_data->dynamic_items[i].text = private_data->dynamic_items[i + 1].text;
				private_data->dynamic_items[i].data = private_data->dynamic_items[i + 1].data;
			}

			/* decide new state & paint widget if needed */
			if (item_index < private_data->item_visible_start) {
				private_data->item_visible_start--;
				private_data->item_focus_index--;
			}
			else if (item_index < (private_data->item_visible_start + private_data->item_visible_nums)) {
				if (private_data->item_actual_nums < (private_data->item_visible_start + private_data->item_visible_nums)) {
					if (private_data->item_visible_start) {
						private_data->item_visible_start--;
						private_data->item_focus_index--;
					}
					else {
						private_data->item_visible_nums--;
						if (private_data->item_focus_index
							&& (private_data->item_focus_index == private_data->item_visible_nums))
						{
							private_data->item_focus_index--;
						}
					}
				}

				/* update widget if needed */
				if (widget->flags & GKT_WIDGET_F_SHOW)
					widget_list_paint(widget, private_data, 1);
			}
		}
		else
			retval = GKT_EPERMIT;
	}
	else
		retval = GKT_EPARAM;

	return retval;
}

void gkt_widget_list_set_focus(gkt_widget_id_t widget_id, 
				uint32_t item_index)
{
	gkt_widget_s *widget;
	widget_list_private_data_s *private_data;

	widget = gkt_widget_by_id(widget_id);
	if (widget) {
		private_data = (widget_list_private_data_s *)widget->private_data;
		if (__LIKELY(private_data) 
			&& (item_index < private_data->item_actual_nums)) 
		{
			widget_list_focus_item(widget, private_data, item_index);
		}
	}
}

void gkt_widget_list_set_focus_by_data(gkt_widget_id_t widget_id,
			uint32_t item_data)
{
	gkt_widget_s *widget;
	widget_list_private_data_s *private_data;
	const gkt_widget_list_item_s *c_items;
	uint32_t item_index;

	widget = gkt_widget_by_id(widget_id);
	if (widget) {
		private_data = (widget_list_private_data_s *)widget->private_data;
		if (__LIKELY(private_data)) {
			if (private_data->const_attr 
				&& private_data->const_attr->const_items)
			{
				c_items = private_data->const_attr->const_items;
			}
			else if (private_data->dynamic_items)
				c_items = private_data->dynamic_items;
			else
				return;

			item_index = 0;
			while (item_index < private_data->item_max_nums) {
				if (c_items[item_index].data == item_data) {
					widget_list_focus_item(widget, private_data, item_index);
					break;
				}
				item_index++;
			}
		}
	}
}

uint32_t gkt_widget_list_get_item_data(
				gkt_widget_id_t widget_id, uint32_t item_index)
{
	gkt_widget_s *widget;
	widget_list_private_data_s *private_data;

	widget = gkt_widget_by_id(widget_id);
	if (widget && widget->private_data) {
		private_data = (widget_list_private_data_s *)widget->private_data;
		if (item_index < private_data->item_actual_nums) {
			if (private_data->dynamic_items)
				return private_data->dynamic_items[item_index].data;
			else if (private_data->const_attr 
				&& private_data->const_attr->item_nums
				&& private_data->const_attr->const_items)
			{
				return private_data->const_attr->const_items[item_index].data;
			}
		}
	}

	return 0;
}

#else

enum {
	WIDGET_LIST_VOICE_ST_IDLE = 0,
	WIDGET_LIST_VOICE_ST_PENDING,
	WIDGET_LIST_VOICE_ST_PLAYING,
	WIDGET_LIST_VOICE_ST_MAX
};

typedef struct _widget_list_private_data {
	const gkt_widget_list_attr_s *const_attr;

	uint32_t	item_max_nums;
	uint32_t	item_actual_nums;

	uint32_t	*item_voice_combined_ids;
	uint32_t	*dynamic_item_datas;

	uint32_t	voice_state;	/* voice state */
	int	voice_request_id;
} widget_list_private_data_s;

static void widget_list_stop_voice(widget_list_private_data_s *private_data)
{
	if (private_data->voice_state != WIDGET_LIST_VOICE_ST_IDLE) {
		private_data->voice_state = WIDGET_LIST_VOICE_ST_IDLE;

		if (private_data->voice_request_id > 0) {
			gkt_window_voice_stop_id(private_data->voice_request_id);
			private_data->voice_request_id = -1;
		}
	}
}

#if defined(GKT_CONFIG_WIDGET_LIST_VOICE_HEAD_INDEX) && GKT_CONFIG_WIDGET_LIST_VOICE_HEAD_INDEX
#define WIDGET_LIST_VOICE_INDEX_FLAG	GKT_WINDOW_VOICE_F_HEAD_INDEX
#else
#define WIDGET_LIST_VOICE_INDEX_FLAG	GKT_WINDOW_VOICE_F_TAIL_PPI
#endif

#if !defined(GKT_CONFIG_WIDGET_LIST_REPEAT_LISTEN) || GKT_CONFIG_WIDGET_LIST_REPEAT_LISTEN
#define WIDGET_LIST_VOICE_REPEAT_LISTEN_FLAG	GKT_WINDOW_VOICE_F_REPEAT_LISTEN
#else
#define WIDGET_LIST_VOICE_REPEAT_LISTEN_FLAG	0
#endif

#define WIDGET_LIST_VOICE_FLAGS	\
	(GKT_WINDOW_VOICE_F_FOLLOWING | GKT_WINDOW_VOICE_F_MULTIPLE_INDEX \
		| WIDGET_LIST_VOICE_INDEX_FLAG | WIDGET_LIST_VOICE_REPEAT_LISTEN_FLAG \
		| GKT_WINDOW_VOICE_F_NOTIFY_START | GKT_WINDOW_VOICE_F_NOTIFY_END \
		| GKT_WINDOW_VOICE_F_WIDGET)

static void widget_list_play_voice(gkt_widget_s *widget,
				widget_list_private_data_s *private_data)
{
	uint32_t voice_flags;
	int retval;

	if (private_data->item_actual_nums > 0) {
		voice_flags = WIDGET_LIST_VOICE_FLAGS;
		voice_flags |= (uint32_t)widget->id << GKT_WINDOW_VOICE_F_WIDGET_ID_SHIFT;
		retval = gkt_window_voice_play_multiple(&private_data->voice_request_id,
						private_data->item_voice_combined_ids,
						private_data->item_actual_nums, voice_flags);
		if (GKT_SUCCESS == retval)
			private_data->voice_state = WIDGET_LIST_VOICE_ST_PLAYING;
		else if (GKT_WINDOW_VOICE_ST_PENDING == retval)
			private_data->voice_state = WIDGET_LIST_VOICE_ST_PENDING;
		else
			private_data->voice_state = WIDGET_LIST_VOICE_ST_IDLE;
	}
}

int gkt_widget_list_event_handler(gkt_widget_s *widget, 
					uint32_t major_type, uint32_t minor_type, 
					uint32_t data)
{
	const gkt_widget_list_attr_s *c_attr;
	widget_list_private_data_s *private_data;
	uint32_t *dynamic_item_datas;
	uint32_t item_nums, size, i;
	int retval = GKT_EVENT_HANDLE_CONTINUE;

	GKT_ASSERT(widget->desc->attr);
	private_data = (widget_list_private_data_s *)widget->private_data;

	if (GKT_EVENT_WIDGET == major_type) {
		retval = GKT_EVENT_HANDLE_COMPLETE;

		if (GKT_WIDGET_EVENT_INIT == minor_type) {
			c_attr = (const gkt_widget_list_attr_s *)widget->desc->attr;
			if (c_attr && c_attr->item_nums && c_attr->const_items) {
				item_nums = c_attr->item_nums;
				if (item_nums > WIDGET_LIST_ITEM_MAXNUMS)
					item_nums = WIDGET_LIST_ITEM_MAXNUMS;
			}
			else 
				item_nums = 0;

			size = sizeof(widget_list_private_data_s) + (sizeof(uint32_t) * item_nums);
			private_data = gkt_malloc(size);
			if (__LIKELY(private_data)) {
				memset((void *)private_data, 0, sizeof(widget_list_private_data_s));

				private_data->const_attr = c_attr;
				private_data->item_max_nums = item_nums;
				private_data->item_actual_nums = item_nums;
				if (item_nums > 0) {
					private_data->item_voice_combined_ids = (uint32_t *)(private_data + 1);
					for (i = 0; i < item_nums; i++)
						private_data->item_voice_combined_ids[i] = c_attr->const_items[i].voice;
				}

				private_data->voice_state = WIDGET_LIST_VOICE_ST_IDLE;
				private_data->voice_request_id = -1;

				widget->private_data = (uint32_t)private_data;
			}
			else
				retval = GKT_ENOMEMORY;
		}
		else if (__LIKELY(private_data)) {
			if (GKT_WIDGET_EVENT_UNINIT == minor_type) {
				if (private_data->dynamic_item_datas) {
					gkt_free(private_data->dynamic_item_datas);
					private_data->dynamic_item_datas = NULL;
				}
				gkt_free(private_data);
			}
			else if (GKT_WIDGET_EVENT_SHOW == minor_type)
				widget_list_play_voice(widget, private_data);
			else if (GKT_WIDGET_EVENT_HIDE == minor_type)
				widget_list_stop_voice(private_data);
			else if (GKT_WIDGET_EVENT_SET_ATTR == minor_type) {
				if (private_data->const_attr 
					&& (private_data->const_attr->item_nums > 0)) 
				{
					gkt_info("list - SET_ATTR: static defined, not permit!\n");
					return GKT_EPERMIT;
				}
				
				item_nums = data;
				if (!item_nums || (item_nums > WIDGET_LIST_ITEM_MAXNUMS)) {
					gkt_trace("list - SET_ATTR: item maxnums should be in range (0, %u]\n", 
									WIDGET_LIST_ITEM_MAXNUMS);
					return GKT_EPARAM;
				}

				if ((private_data->dynamic_item_datas)
					&& (item_nums <= private_data->item_max_nums)) 
				{
					private_data->item_actual_nums = 0;
					return GKT_EVENT_HANDLE_COMPLETE;
				}

				size = sizeof(uint32_t) * item_nums * 2;
				dynamic_item_datas = (uint32_t *)gkt_malloc(size);
				if (dynamic_item_datas) {
					/* free old dynamic items */
					if (private_data->dynamic_item_datas)
						gkt_free(private_data->dynamic_item_datas);

					private_data->item_actual_nums = 0;
					private_data->item_max_nums = item_nums;
					private_data->dynamic_item_datas = dynamic_item_datas;
					private_data->item_voice_combined_ids = dynamic_item_datas + item_nums;
				}
				else {
					gkt_trace("list - SET_ATTR: no memory to allocate dynamic items!\n");
					retval = GKT_ENOMEMORY;
				}
			}
		}
	}
	else if (GKT_EVENT_VOICE == major_type) {
		gkt_trace("widget_list: <VOICE> state(%u), type(%u), id(%d @ %u)\n",
					private_data->voice_state, minor_type,
					private_data->voice_request_id, data);

		if ((private_data->voice_request_id > 0)
			&& ((int)data == private_data->voice_request_id))
		{
			if (GKT_WINDOW_VOICE_EVENT_PLAY_START == minor_type) {
				if (WIDGET_LIST_VOICE_ST_PENDING == private_data->voice_state)
					private_data->voice_state = WIDGET_LIST_VOICE_ST_PLAYING;
			}
			else {
				private_data->voice_state = WIDGET_LIST_VOICE_ST_IDLE;
				private_data->voice_request_id = -1;
			}
		}
		else {
			gkt_error("widget_list: voice_request_id invalid!\n");
		}

		retval = GKT_EVENT_HANDLE_COMPLETE;
	}
	else if (GKT_EVENT_KEY == major_type) {
		if (private_data && private_data->item_actual_nums) {
			if ((minor_type >= GKT_KEY_1)
				&& (minor_type < (GKT_KEY_1 + private_data->item_actual_nums)))
			{
				retval = gkt_window_custom_event_handle(widget->window, 
									GKT_EVENT_WIDGET, GKT_WIDGET_EVENT_CLICK, 
									(uint32_t)widget->id, 
									(uint32_t)(minor_type - GKT_KEY_1));
			}
#if GKT_WINDOW_VOICE_REPEAT_LISTEN
			else if (GKT_KEY_0 == minor_type) {
				if (WIDGET_LIST_VOICE_ST_PLAYING == private_data->voice_state)
					widget_list_stop_voice(private_data);
				else if (WIDGET_LIST_VOICE_ST_PENDING == private_data->voice_state)
					return GKT_EVENT_HANDLE_COMPLETE;
				widget_list_play_voice(widget, private_data);

				retval = GKT_EVENT_HANDLE_COMPLETE;
			}
#endif
		}
	}

	return retval;
}

int gkt_widget_list_add_item(gkt_widget_id_t widget_id,
				const gkt_widget_list_item_s *item)
{
	gkt_widget_s *widget;
	widget_list_private_data_s *private_data;
	uint32_t item_index;
	int retval = GKT_SUCCESS;

	widget = gkt_widget_by_id(widget_id);
	if (widget && item) {
		private_data = (widget_list_private_data_s *)widget->private_data;
		if (private_data 
			&& private_data->dynamic_item_datas
			&& (private_data->item_actual_nums < private_data->item_max_nums)) 
		{
			item_index = private_data->item_actual_nums++;
			private_data->item_voice_combined_ids[item_index] = item->voice;
			private_data->dynamic_item_datas[item_index] = item->data;
		}
		else
			retval = GKT_EPERMIT;
	} 
	else
		retval = GKT_EPARAM;

	return retval;
}

int gkt_widget_list_delete_item(gkt_widget_id_t widget_id, 
				uint32_t item_index)
{
	gkt_widget_s *widget;
	widget_list_private_data_s *private_data;
	uint32_t i;
	int retval = GKT_SUCCESS;

	widget = gkt_widget_by_id(widget_id);
	if (widget) {
		private_data = (widget_list_private_data_s *)widget->private_data;
		if (private_data
			&& private_data->dynamic_item_datas
			&& (item_index < private_data->item_actual_nums)) 
		{
			/* delete item from list */
			private_data->item_actual_nums--;
			for (i = item_index; i < private_data->item_actual_nums; i++) {
				private_data->item_voice_combined_ids[i] = private_data->item_voice_combined_ids[i + 1];
				private_data->dynamic_item_datas[i] = private_data->dynamic_item_datas[i + 1];
			}
		}
		else
			retval = GKT_EPERMIT;
	}
	else
		retval = GKT_EPARAM;

	return retval;
}

uint32_t gkt_widget_list_get_item_data(
				gkt_widget_id_t widget_id, uint32_t item_index)
{
	gkt_widget_s *widget;
	widget_list_private_data_s *private_data;

	widget = gkt_widget_by_id(widget_id);
	if (widget && widget->private_data) {
		private_data = (widget_list_private_data_s *)widget->private_data;
		if (item_index < private_data->item_actual_nums) {
			if (private_data->dynamic_item_datas)
				return private_data->dynamic_item_datas[item_index];
			else if (private_data->const_attr 
				&& private_data->const_attr->item_nums
				&& private_data->const_attr->const_items)
			{
				return private_data->const_attr->const_items[item_index].data;
			}
		}
	}

	return 0;
}

#endif

uint32_t gkt_widget_list_get_item_nums(gkt_widget_id_t widget_id)
{
	gkt_widget_s *widget;
	widget_list_private_data_s *private_data;

	widget = gkt_widget_by_id(widget_id);
	if (widget && widget->private_data) {
		private_data = (widget_list_private_data_s *)widget->private_data;
		return private_data->item_actual_nums;
	}

	return 0;
}

